package com.danguo.happymonkey;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.opengl.GLES20;
import android.util.Log;

public class Line
{
   private FloatBuffer theVertexBuffer;

   private final String theVertexShaderCode =
           // This matrix member variable provides a hook to manipulate
           // the coordinates of the objects that use this vertex shader
           "uniform mat4 uMVPMatrix;" +

           "attribute vec4 vPosition;" +
           "void main() {" +
           // the matrix must be included as a modifier of gl_Position
           "  gl_Position = uMVPMatrix * vPosition;" +
           "}";

   private final String theFragmentShaderCode =
           "precision mediump float;" +
           "uniform vec4 vColor;" +
           "void main() {" +
           "  gl_FragColor = vColor;" +
           "}";

   protected int theProgramHandle;
   protected int thePositionHandle;
   protected int theColorHandle;
   protected int theMVPMatrixHandle;

   // number of coordinates per vertex in this array
   static final int COORDS_PER_VERTEX = 3;
   static float LineCoords[] = {
       0.0f, 0.0f, 0.0f,
       1.0f, 0.0f, 0.0f
   };

   private final int VertexCount = LineCoords.length / COORDS_PER_VERTEX;
   private final int VertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

   // Set color with red, green, blue and alpha (opacity) values
   float color[] = { 0.0f, 0.0f, 0.0f, 1.0f };

   public Line(){
       // initialize vertex byte buffer for shape coordinates
       ByteBuffer bb = ByteBuffer.allocateDirect(
               // (number of coordinate values * 4 bytes per float)
               LineCoords.length * 4);
       // use the device hardware's native byte order
       bb.order(ByteOrder.nativeOrder());

       // create a floating point buffer from the ByteBuffer
       theVertexBuffer = bb.asFloatBuffer();
       // add the coordinates to the FloatBuffer
       theVertexBuffer.put(LineCoords);
       // set the buffer to read the first coordinate
       theVertexBuffer.position(0);


       theProgramHandle = createProgram( theVertexShaderCode, theFragmentShaderCode );

   }

   public void setVerts(float v0, float v1, float v2, float v3, float v4, float v5)
   {
       LineCoords[0] = v0;
       LineCoords[1] = v1;
       LineCoords[2] = v2;
       LineCoords[3] = v3;
       LineCoords[4] = v4;
       LineCoords[5] = v5;

       theVertexBuffer.put(LineCoords);
       // set the buffer to read the first coordinate
       theVertexBuffer.position(0);

   }

   public void setColor(float red, float green, float blue, float alpha)
   {
       color[0] = red;
       color[1] = green;
       color[2] = blue;
       color[3] = alpha;
   }
   
   public void setAlpha( float anAlpha )
   {
      color[3] = anAlpha;
   }

   public void draw( float[] mvpMatrix ) {
       // Add program to OpenGL ES environment
       GLES20.glUseProgram(theProgramHandle);
       checkGlError("set Program");
       // get handle to vertex shader's vPosition member
       thePositionHandle = GLES20.glGetAttribLocation(theProgramHandle, "vPosition");
       checkGlError("get Position Handle");

       // Enable a handle to the triangle vertices
       GLES20.glEnableVertexAttribArray(thePositionHandle);
       checkGlError("enable vertex attrib");

       // Prepare the triangle coordinate data
       GLES20.glVertexAttribPointer(thePositionHandle, COORDS_PER_VERTEX,
                                    GLES20.GL_FLOAT, false,
                                    VertexStride, theVertexBuffer);
       checkGlError("set vertex attrib");

       // get handle to fragment shader's vColor member
       theColorHandle = GLES20.glGetUniformLocation(theProgramHandle, "vColor");
       checkGlError("glGetUniformLocation Color");

       // Set color for drawing the triangle
       GLES20.glUniform4fv(theColorHandle, 1, color, 0);      
       checkGlError("set Color");

       // get handle to shape's transformation matrix
       theMVPMatrixHandle = GLES20.glGetUniformLocation(theProgramHandle, "uMVPMatrix");
      checkGlError("glGetUniformLocation Matrix");

       // Apply the projection and view transformation
       GLES20.glUniformMatrix4fv(theMVPMatrixHandle, 1, false, mvpMatrix, 0);
       checkGlError("glUniformMatrix4fv");


       // Draw the triangle
       GLES20.glDrawArrays(GLES20.GL_LINES, 0, VertexCount);

       // Disable vertex array
       GLES20.glDisableVertexAttribArray(thePositionHandle);
   }
   
   
/********************* UTILITY METHODS ****************************************   
   
   
   /**
    * Loads and compiles a shader
    * 
    * @param shaderType
    *           integer from GLES20 representing the type of shader
    * @param source
    *           the C code describing the shader
    * @return the handle to the compiled shader
    */
   private int loadShader( int shaderType, String source )
   {
      int shader = GLES20.glCreateShader( shaderType );
      if( shader != 0 )
      {
         GLES20.glShaderSource( shader, source );
         GLES20.glCompileShader( shader );
         int[] compiled = new int[1];
         GLES20.glGetShaderiv( shader, GLES20.GL_COMPILE_STATUS, compiled, 0 );
         if( compiled[0] == 0 )
         {
            Log.e( "GLES20Points", "Could not compile shader " + shaderType
                  + ":" );
            Log.e( "GLES20Points", GLES20.glGetShaderInfoLog( shader ) );
            GLES20.glDeleteShader( shader );
            shader = 0;
         }
      }
      return shader;
   }

   /**
    * Combines vertex and fragment shaders into a compiled program
    * 
    * @param vertexSource
    *           the C code describing the vertex shader
    * @param fragmentSource
    *           the C code describing the fragment shader
    * @return a handle to the compiled program
    */
   private int createProgram( String vertexSource, String fragmentSource )
   {
      int vertexShader = loadShader( GLES20.GL_VERTEX_SHADER, vertexSource );
      if( vertexShader == 0 )
      {
         return 0;
      }

      int pixelShader = loadShader( GLES20.GL_FRAGMENT_SHADER, fragmentSource );
      if( pixelShader == 0 )
      {
         return 0;
      }

      int program = GLES20.glCreateProgram();
      if( program != 0 )
      {
         GLES20.glAttachShader( program, vertexShader );
         checkGlError( "glAttachShader" );
         GLES20.glAttachShader( program, pixelShader );
         checkGlError( "glAttachShader" );
         GLES20.glLinkProgram( program );
         int[] linkStatus = new int[1];
         GLES20.glGetProgramiv( program, GLES20.GL_LINK_STATUS, linkStatus, 0 );
         if( linkStatus[0] != GLES20.GL_TRUE )
         {
            Log.e( "GLES20Points", "Could not link program: " );
            Log.e( "GLES20Points", GLES20.glGetProgramInfoLog( program ) );
            GLES20.glDeleteProgram( program );
            program = 0;
         }
      }
      return program;
   }

   /**
    * Checks GL for errors
    * 
    * @param op
    *           String to send to the log if an error is found.
    */
   private void checkGlError( String op )
   {
      int error;
      while( (error = GLES20.glGetError()) != GLES20.GL_NO_ERROR )
      {
         Log.e( "GLES20Points", op + ": glError " + error );
         throw new RuntimeException( op + ": glError " + error );
      }
   }
}
